﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Subtle.Domain.Services.Interfaces;
using Subtle.Domain.Models;
using log4net;
using Subtle.Data.Persistence.Interfaces;
using System.Reflection;
using Subtle.Data.Extensions;
using System.Collections;

namespace Subtle.Domain.Services
{
	/// <summary>
	/// Post Service
	/// </summary>
	public class PostService : IPostService
	{
		private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

		private IRepository repository;

		/// <summary>
		/// User Service
		/// </summary>
		/// <param name="postUnitOfWork"></param>
		/// <param name="categoryUnitOfWork"></param>
		/// <param name="tagUnitOfWork"></param>
		public PostService(IRepository repository)
		{
			this.repository = repository;
		}

		/// <summary>
		/// Create Post
		/// </summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public bool SavePost(Post post)
		{
			//Ensure that the user is valid
			if (post.User == null)
			{
				return false;
			}
			if (!post.User.Verified)
			{
				return false;
			}

			//Let's get the category
			post.Category = GetCategory(post.Category.Description);

			//Let's get the Tags
			IList<Tag> tags = new List<Tag>();
			foreach (Tag tag in post.Tags)
			{
				tags.Add(GetTag(tag.Description));
			}
			post.Tags = tags;

			return repository.Save<Post>(post);
		}

		/// <summary>
		/// Get Post
		/// </summary>
		/// <param name="postID"></param>
		/// <returns></returns>
		public Post GetPost(int postID)
		{
			return (from post in repository.Query<Post>()
					where post.ID == postID
					select post).FirstOrDefault();
		}

		/// <summary>
		/// Get Category
		/// </summary>
		/// <param name="categoryDescription"></param>
		/// <returns></returns>
		public Category GetCategory(string categoryDescription)
		{
			Category foundCategory = (from category in repository.Query<Category>()
									  where category.Description == categoryDescription
									  select category).FirstOrDefault();
			if (foundCategory == null)
			{
				foundCategory = new Category
				{
					Description = categoryDescription
				};
			}
			return foundCategory;
		}

		/// <summary>
		/// Get Categories
		/// </summary>
		/// <returns></returns>
		public Category[] GetCategories()
		{
			return repository.Query<Category>().ToArray();
		}

		/// <summary>
		/// Create Category
		/// </summary>
		/// <param name="description"></param>
		/// <returns></returns>
		public bool CreateCategory(string description)
		{
			var categoryExistsQuery = GetCategory(description);
			if (categoryExistsQuery == null)
			{
				var category = new Category
				{
					Description = description,
					DateCreated = DateTime.Now
				};
				repository.Save<Category>(category);
				return true;
			}
			return false;
		}

		/// <summary>
		/// Get Tags
		/// </summary>
		/// <param name="tagDescription"></param>
		/// <returns></returns>
		public Tag GetTag(string tagDescription)
		{
			Tag foundTag = (from tag in repository.Query<Tag>()
							where tag.Description == tagDescription
							select tag).FirstOrDefault();
			if (foundTag == null)
			{
				foundTag = new Tag
				{
					Description = tagDescription
				};
			}
			return foundTag;
		}

		/// <summary>
		/// Get Tags
		/// </summary>
		/// <returns></returns>
		public Tag[] GetTags()
		{
			return repository.Query<Tag>().ToArray();
		}

		/// <summary>
		/// Get Post
		/// </summary>
		/// <param name="year"></param>
		/// <param name="month"></param>
		/// <returns></returns>
		public IList<Post> GetPosts(int year, int month)
		{
			return (from post in repository.Query<Post>()
					where post.Active &&
					post.PublishDate.Year == year &&
					post.PublishDate.Month == month
					select post).ToList<Post>();
		}

		/// <summary>
		/// Get Post
		/// </summary>
		/// <param name="linkTitle"></param>
		/// <returns></returns>
		public Post GetPost(string linkTitle)
		{
			return (from post in repository.Query<Post>()
					where post.SanitizedTitle == linkTitle.Sanitize()
					select post).FirstOrDefault();
		}

		/// <summary>
		/// Create Tag
		/// </summary>
		/// <param name="description"></param>
		/// <returns></returns>
		public bool CreateTag(string description)
		{
			var tagExistsQuery = GetTag(description);
			if (tagExistsQuery == null)
			{
				var tag = new Tag
				{
					Description = description,
					DateCreated = DateTime.Now
				};
				repository.Save<Tag>(tag);
				return true;
			}
			return false;
		}

		/// <summary>
		/// Get Latest Published Posts
		/// </summary>
		/// <param name="numberOfPosts"></param>
		/// <returns></returns>
		public IList<Post> GetLatestPublishedPosts(int numberOfPosts)
		{
			return (from post in repository.Query<Post>()
					where post.Active &&
					post.PublishDate <= DateTime.Now
					orderby post.PublishDate descending
					select post).Take(numberOfPosts).ToList<Post>();
		}

		/// <summary>
		/// Get Archives
		/// </summary>
		/// <returns>List of Archives, the content being DateTime</returns>
		public IDictionary<DateTime, int> GetArchives()
		{
			return (from post in repository.Query<Post>()
					where post.Active
					group post by new { post.PublishDate.Year, post.PublishDate.Month } into g
					select new { YearAndMonth = g.Key, Count = g.Count() })
			.ToDictionary(archive => new DateTime(archive.YearAndMonth.Year, archive.YearAndMonth.Month, 1), archive => archive.Count);
		}

		/// <summary>
		/// Delete Post
		/// </summary>
		/// <param name="post"></param>
		/// <returns></returns>
		public bool DeletePost(Post post)
		{
			post.Active = false;
			return repository.Save<Post>(post);
		}
	}
}
